#include <winsock2.h>
#include <ws2tcpip.h>

#include <cassert>
#include <iostream>
#include <sstream>
#include <thread>
using namespace std;

void send(SOCKET sock, const char buf[], int total_bytes) {
  int sent = 0;
  while(sent < total_bytes) {
    // 不能保证数据全部发出，简单办法是用循环、递进发送，直到全部发出。
    // 注意不是重复参数，而是根据已送出的情况调整缓冲区指针、待发送的字节数。
    int nb = ::send(sock, buf + sent, total_bytes - sent, 0);
    if(nb == SOCKET_ERROR) {
      cerr << "send data failed" << endl;
      return;
    }
    sent += nb;
  }
}

int main() {
  WORD sock_version = MAKEWORD(2, 2);
  WSADATA wd = {}; // 空括号重要！或用memset()
  if(auto e = WSAStartup(sock_version, &wd); e) {
    printf("WSAStartup() failed: %d\n", e);
    return 1;
  }

  SOCKET sock_main = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  assert(sock_main != INVALID_SOCKET);

  sockaddr_in sock_addr = {}; // _in是internet的意思
  sock_addr.sin_family = AF_INET;
  sock_addr.sin_port = htons(8888); // 字节序：host-to-net, short
  sock_addr.sin_addr.S_un.S_addr = INADDR_ANY; // 所有地址
  if (auto e = bind(sock_main, (sockaddr*)&sock_addr, sizeof(sock_addr));
      e == SOCKET_ERROR) {
    printf("bind() failed: %d\n", e);
    return 1;
  }

  // 进入侦听状态
  if(auto e = listen(sock_main, 5); e == SOCKET_ERROR) {
    printf("listen() failed: %d\n", e);
    return 1;
  }

  const string body = R"(<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>web-00</title>
</head>
<body>
  <h1>Hello, Web!</h1>
</body>
</html>
)";

  ostringstream oss;
  oss << R"(HTTP/1.1 200 OK\r\nContentType: text/html; charset=utf-8\r\nContent-Length: )"
      << body.size() << "\r\n"
      << "Date: Sun, 12 May 2024 23:23:59 GMT\r\n"
      << "\r\n";
  const string head = oss.str();

  // 接受客户端连接
  for(;;) {
    printf("listening ...\n");
    // 典型模式：从系统获取信息时，先计算缓冲区长度（字节数），告诉系统，
    // 系统写入信息，再记录写入的字节数。
    sockaddr_in client_addr = {};
    int len = sizeof(client_addr);
    SOCKET client_sock = accept(sock_main, (sockaddr*)&client_addr, &len);
    if(client_sock == INVALID_SOCKET) {
      printf("accept() failed()\n");
      continue; // 忽略此错误
    }
    printf("client %s connected.\n", inet_ntoa(client_addr.sin_addr));
    send(client_sock, head.c_str(), head.size());
    send(client_sock, body.c_str(), body.size());

    closesocket(client_sock); // 关闭与客户端的连接

    // this_thread::sleep_for(3000ms);
    // 注意是::域解析符。注意时间的写法，ms后缀是自定义字面量，很新的C++特性
  }

  WSACleanup(); // 清理，与Startup()对称
}